package queue

import (
	"errors"
	"fmt"
	"sync"
	"time"
)

//DelayMessage - 延迟消息
//参考环形队列设计 3600slots for one hour
type DelayMessage struct {
	cursorPos int                    //current position
	slots     [3600]map[string]*Task // 3600 slots for one hour
	closed    chan bool              //dm close signal
	taskClose chan bool              // task loop  stop singal
	timeClose chan bool              // time loop stop singal
	startTime time.Time              //启动时间
	mu        sync.Mutex             //mutex 解决并发读写场景
}

//TaskFunc - 任务实际动作
type TaskFunc func(args ...interface{})

//Task - slots上的任务
type Task struct {
	cycleNum int
	doAction TaskFunc
	params   []interface{}
}

//Close : 延迟消息服务关闭
func (dm *DelayMessage) Close() {
	dm.closed <- true
}

// TaskLoop : 任务检查器
func (dm *DelayMessage) TaskLoop() {
	defer func() {
		fmt.Println("task loop exit")
	}()

	for {
		select {
		case <-dm.taskClose:
			return

		default:

			tasks := dm.slots[dm.cursorPos]

			for k, v := range tasks {
				if v.cycleNum == 0 {
					go v.doAction(v.params...)
					delete(tasks, k)
				} else {
					v.cycleNum--
				}
			}

		}
	}
}

//TimeLoop : 时间循环器
func (dm *DelayMessage) TimeLoop() {
	defer func() {
		fmt.Println("time loop exit")
	}()

	tick := time.NewTicker(time.Second) //秒定时器

	for {
		select {
		case <-dm.timeClose:
			return
		case <-tick.C:
			// fmt.Println("当前时间:", time.Now().Format(timeFormat))
			dm.mu.Lock()                             //加锁解决并发
			dm.cursorPos = (dm.cursorPos + 1) % 3600 //compute the new index
			dm.mu.Unlock()
		}
	}

}

//Add : 添加任务
//param delayDuration 间隔时间，单位秒
//param key 任务标识
//param doAction 执行任务函数
//param params  参数列表，根据doAction
func (dm *DelayMessage) Add(delaySeconds int, key string, doAction TaskFunc, params []interface{}) error {

	//计算圈数
	cycleNum := delaySeconds / 3600 //cycleNum取相对值，因为每圈访问都是递减cycleNum

	dm.mu.Lock()
	//计算slots位置,要注意当前cursorPos的值，因为服务启动cursorPos一直在走
	idx := (dm.cursorPos + delaySeconds) % 3600
	dm.mu.Unlock()
	taskMap := dm.slots[idx]

	if _, ok := taskMap[key]; ok {
		return errors.New("the key exists")
	}

	//添加任务
	taskMap[key] = &Task{
		cycleNum: cycleNum,
		doAction: doAction,
		params:   params,
	}

	return nil
}

//Start : 启动延迟消息服务
func (dm *DelayMessage) Start() {
	//启动任务循环
	go dm.TaskLoop()
	//启动定时器
	go dm.TimeLoop()

	//启动巡检各开关
	go func() {
		for {
			t1 := time.NewTicker(time.Second * 5)
			select {
			case <-dm.closed:
				dm.taskClose <- true //关闭任务循环
				dm.timeClose <- true //关闭定时器
			case <-t1.C:
				//巡检间隔
			}

		}

	}()

}

//NewDelayMessage : 获取一个延迟消息对象
func NewDelayMessage() *DelayMessage {
	dm := &DelayMessage{
		cursorPos: 0,
		closed:    make(chan bool),
		taskClose: make(chan bool),
		timeClose: make(chan bool),
	}

	//初始化构建slots
	for i := 0; i < 3600; i++ {
		dm.slots[i] = make(map[string]*Task)
	}

	return dm
}
